﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SceneLib;
using SplineLib;
using Tao.OpenGl;
using System.Runtime.InteropServices;

namespace OpenGL
{
    class SkyBox
    {
        private SceneMaterial skyMaterial;
        private float radius;
        private Vector center;
        private byte[, ,] textureImage;
        private int size;
        
        public SkyBox(SceneMaterial material, SplineInfo spline)
        {
            float radiusx = 0.5f * Math.Abs(spline.XMax - spline.XMin);
            float radiusy = 0.5f * Math.Abs(spline.YMax - spline.YMin);
            float radiusz = 0.5f * Math.Abs(spline.ZMax - spline.ZMin);

            skyMaterial = material;

            if (radiusx >= radiusy && radiusx >= radiusz)
            {
                radius = radiusx;
            }
            else if (radiusy >= radiusx && radiusy >= radiusz)
            {
                radius = radiusy;
            }
            else
            {
                radius = radiusz;
            }

            center = new Vector(spline.XMax + spline.XMin,
                                spline.YMax + spline.YMin,
                                spline.ZMax + spline.ZMin);
            radius = 25.0f * radius;

            center = 0.5f * center;
            size = 512;

            textureImage = new byte[size, size, 4];

            int i, j;
            PerlingNoise n = new PerlingNoise(new Vector(0.3f, 0.3f, 0.9f),
                                               new Vector(1.0f, 1.0f, 1.0f));           

            for (i = 0; i < size; i++)
            {
                for (j = 0; j < size; j++)
                {
                    Vector c = n.getColor(i, j);

                    textureImage[i, j, 0] = (byte)(255 * c.x);
                    textureImage[i, j, 1] = (byte)(255 * c.y);
                    textureImage[i, j, 2] = (byte)(255 * c.z);
                    textureImage[i, j, 3] = 255;
                }
            }
        }

        public void Draw()
        {
            Gl.glMaterialfv(Gl.GL_FRONT_AND_BACK, Gl.GL_SPECULAR,
                            MathUtils.GetVector3Array(skyMaterial.Specular));
            Gl.glMaterialf(Gl.GL_FRONT_AND_BACK, Gl.GL_SHININESS,
                           skyMaterial.Shininess);
            Gl.glColor3fv(MathUtils.GetVector3Array(skyMaterial.Diffuse));
            SetTextures(skyMaterial);

            Gl.glBegin(Gl.GL_QUADS);

            Gl.glTexCoord2f(0.5f, 0.0f);
            Gl.glNormal3f(0.0f, 0.0f, 1.0f);
            Gl.glVertex3f(center.x + radius, center.y - radius, center.z - radius);
            Gl.glTexCoord2f(0.0f, 0.0f);
            Gl.glNormal3f(0.0f, 0.0f, 1.0f);
            Gl.glVertex3f(center.x - radius, center.y - radius, center.z - radius);
            Gl.glTexCoord2f(0.0f, 1.0f);
            Gl.glNormal3f(0.0f, 0.0f, 1.0f);
            Gl.glVertex3f(center.x - radius, center.y + radius, center.z - radius);
            Gl.glTexCoord2f(0.5f, 1.0f);
            Gl.glNormal3f(0.0f, 0.0f, 1.0f);
            Gl.glVertex3f(center.x + radius, center.y + radius, center.z - radius);

            Gl.glEnd();

            Gl.glBegin(Gl.GL_QUADS);

            Gl.glTexCoord2f(1.0f, 0.0f);
            Gl.glNormal3f(1.0f, 0.0f, 0.0f);
            Gl.glVertex3f(center.x + radius, center.y - radius, center.z + radius);
            Gl.glTexCoord2f(0.5f, 0.0f);
            Gl.glNormal3f(1.0f, 0.0f, 0.0f);
            Gl.glVertex3f(center.x + radius, center.y - radius, center.z - radius);
            Gl.glTexCoord2f(0.5f, 1.0f);
            Gl.glNormal3f(1.0f, 0.0f, 0.0f);
            Gl.glVertex3f(center.x + radius, center.y + radius, center.z - radius);
            Gl.glTexCoord2f(1.0f, 1.0f);
            Gl.glNormal3f(1.0f, 0.0f, 0.0f);
            Gl.glVertex3f(center.x + radius, center.y + radius, center.z + radius);

            Gl.glEnd();

            Gl.glBegin(Gl.GL_QUADS);

            Gl.glTexCoord2f(0.5f, 0.0f);
            Gl.glNormal3f(0.0f, 0.0f, 1.0f);
            Gl.glVertex3f(center.x - radius, center.y - radius, center.z + radius);
            Gl.glTexCoord2f(1.0f, 0.0f);
            Gl.glNormal3f(0.0f, 0.0f, 1.0f);
            Gl.glVertex3f(center.x + radius, center.y - radius, center.z + radius);
            Gl.glTexCoord2f(1.0f, 1.0f);
            Gl.glNormal3f(0.0f, 0.0f, 1.0f);
            Gl.glVertex3f(center.x + radius, center.y + radius, center.z + radius);
            Gl.glTexCoord2f(0.5f, 1.0f);
            Gl.glNormal3f(0.0f, 0.0f, 1.0f);
            Gl.glVertex3f(center.x - radius, center.y + radius, center.z + radius);

            Gl.glEnd();

            Gl.glBegin(Gl.GL_QUADS);

            Gl.glTexCoord2f(0.0f, 0.0f);
            Gl.glNormal3f(1.0f, 0.0f, 0.0f);
            Gl.glVertex3f(center.x - radius, center.y - radius, center.z - radius);
            Gl.glTexCoord2f(0.5f, 0.0f);
            Gl.glNormal3f(1.0f, 0.0f, 0.0f);
            Gl.glVertex3f(center.x - radius, center.y - radius, center.z + radius);
            Gl.glTexCoord2f(0.5f, 1.0f);
            Gl.glNormal3f(1.0f, 0.0f, 0.0f);
            Gl.glVertex3f(center.x - radius, center.y + radius, center.z + radius);
            Gl.glTexCoord2f(0.0f, 1.0f);
            Gl.glNormal3f(1.0f, 0.0f, 0.0f);
            Gl.glVertex3f(center.x - radius, center.y + radius, center.z - radius);

            Gl.glEnd();

            Gl.glBegin(Gl.GL_QUADS);

            Gl.glTexCoord2f(0.5f, 1.0f);
            Gl.glNormal3f(0.0f, -1.0f, 0.0f);
            Gl.glVertex3f(center.x - radius, center.y + radius, center.z - radius);
            Gl.glTexCoord2f(0.5f, 0.5f);
            Gl.glNormal3f(0.0f, -1.0f, 0.0f);
            Gl.glVertex3f(center.x - radius, center.y + radius, center.z + radius);
            Gl.glTexCoord2f(1.0f, 0.5f);
            Gl.glNormal3f(0.0f, -1.0f, 0.0f);
            Gl.glVertex3f(center.x + radius, center.y + radius, center.z + radius);
            Gl.glTexCoord2f(1.0f, 1.0f);
            Gl.glNormal3f(0.0f, -1.0f, 0.0f);
            Gl.glVertex3f(center.x + radius, center.y + radius, center.z - radius);

            Gl.glEnd();
        }

        private void SetTextures(SceneMaterial sceneMat)
        {
            Gl.glEnable(Gl.GL_TEXTURE_2D);


            Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAG_FILTER, Gl.GL_NEAREST);
            Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MIN_FILTER, Gl.GL_NEAREST);
            Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_S, Gl.GL_CLAMP);
            Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_T, Gl.GL_CLAMP);

            Gl.glTexImage2D(Gl.GL_TEXTURE_2D, 0, Gl.GL_RGBA, size,
                         size, 0, Gl.GL_RGBA, Gl.GL_UNSIGNED_BYTE, textureImage);
        }
    }
}
